package com.chencs.zuul.filter;


import java.io.IOException;
import java.util.Enumeration;
import java.util.List;

import javax.servlet.http.HttpServletRequest;

import org.apache.http.HttpEntity;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.config.RequestConfig.Builder;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.BufferedHttpEntity;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.http.HttpHeaders;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;

public class DevRouterFilter extends ZuulFilter {
	Logger logger = LoggerFactory.getLogger(DevRouterFilter.class);
	
	@Autowired  
    DiscoveryClient discoveryClient;
	
	int connectTimeout = 5000;
	
	int socketTimeout = 30000;
	
	@Override
	public Object run() {
		logger.info("i'm route filter of zuul");
		String rsp = null;
		RequestContext ctx = RequestContext.getCurrentContext();
		HttpServletRequest request = ctx.getRequest();
		String uri = request.getRequestURI();
		logger.info("request to {}", uri);
		String host = request.getRemoteHost();
		logger.info("request host is {}", host);
		String serviceId = null; //获取Service-common
		if(uri.startsWith("/")) {
			uri = uri.substring(1);
		}
		serviceId = uri.substring(0, uri.indexOf("/"));
		
		logger.info("service id {}", serviceId);
		

        
        
        List<ServiceInstance> list = discoveryClient.getInstances(serviceId);
        logger.info("service id {} size {}", serviceId, list.size());
        ServiceInstance remoteSI = null;
        for(ServiceInstance si : list) {  
        	logger.debug("services:" + serviceId + ":getHost()=" + si.getHost());  
        	logger.debug("services:" + serviceId + ":getPort()=" + si.getPort());  
        	logger.debug("services:" + serviceId + ":getServiceId()=" + si.getServiceId());  
        	logger.debug("services:" + serviceId + ":getUri()=" + si.getUri());  
        	logger.debug("services:" + serviceId + ":getMetadata()=" + si.getMetadata()); 
        	
        	if(si.getHost().equalsIgnoreCase(host) || ((host.equals("localhost") || host.equals("127.0.0.1")) && (si.getHost().equals("localhost") || si.getHost().equals("127.0.0.1")))) {
        		remoteSI = si;
        		break;
        	}
        }
        String remoteUri = remoteSI.getUri() + "/" + uri.substring(uri.indexOf("/"));
		logger.info("remoteUri : {}", remoteUri);
		logger.info("request method: {}", request.getMethod());
		if(request.getMethod().equalsIgnoreCase("post")) {
			rsp = post(request, remoteUri + getParameterString(request));
		} else {
			rsp = get(request, remoteUri + getParameterString(request));
		}
		
		logger.info("rsp string : {}",  rsp);
		return rsp;
	}
	
	public String getParameterString(HttpServletRequest request){
		StringBuffer sb = new StringBuffer();
		Enumeration<String> names = request.getParameterNames();
		String name = "";
		while(names.hasMoreElements()) {
			name = names.nextElement();
			if(sb.length() == 0) sb.append("?");
			else sb.append("&");
			sb.append(name).append("=").append(request.getParameter(name));
		}
		return sb.toString();
	}
	
	public String post(HttpServletRequest req, String serverURL) {
		String bodyContent = null;
		CloseableHttpClient hc = null;
		CloseableHttpResponse chr = null;
		try {
			hc = HttpClients.createDefault();
			HttpPost httpPost = new HttpPost(serverURL);
			Enumeration<String> headerNames = req.getHeaderNames();
            String headername;
            while (headerNames.hasMoreElements()) {
                headername = headerNames.nextElement();
                if ("host".equalsIgnoreCase(headername)) continue;
                if (headername.equalsIgnoreCase(HttpHeaders.CONTENT_LENGTH)) continue;
                httpPost.addHeader(headername, req.getHeader(headername));
                logger.debug("add normal post request header:" + headername + "=" + req.getHeader(headername));
            }
            HttpEntity entity = new BufferedHttpEntity(new InputStreamEntity(req.getInputStream(), req.getContentLength()));
			httpPost.setEntity(entity);
			logger.debug("normal post request Entity:" + EntityUtils.toString(httpPost.getEntity()));
	        logger.debug("normal post request ContentLength:" + req.getContentLength() + ",Entity ContentLength:" + httpPost.getEntity().getContentLength());

			Builder bd = null;
			if (this.connectTimeout > 0) {
				bd = bd == null ? RequestConfig.custom() :bd;
				bd.setConnectTimeout(connectTimeout);
			}
			if(this.socketTimeout > 0){
				(bd = bd == null ? RequestConfig.custom() :bd).setSocketTimeout(socketTimeout);
			}
			if(bd != null){
				httpPost.setConfig(bd.build());
			}
			chr = hc.execute(httpPost);
			HttpEntity he = chr.getEntity();
			if (he != null) {
				bodyContent = EntityUtils.toString(he,"utf-8");
			}
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		} finally {
			if (hc != null) {
				try {
					hc.close();
				} catch (IOException e) {
				}
			}
			if (chr != null) {
				try {
					chr.close();
				} catch (IOException e) {
				}
			}
		}

		logger.info("post : {}",  bodyContent);
		return bodyContent;
	};
	
	public String get(HttpServletRequest req, String serverURL) {
		String bodyContent = null;
		CloseableHttpClient hc = null;
		CloseableHttpResponse chr = null;
		try {
			hc = HttpClients.createDefault();
			HttpGet httpGet = new HttpGet(serverURL);
			Enumeration<String> headerNames = req.getHeaderNames();
            String headername;
            while (headerNames.hasMoreElements()) {
                headername = headerNames.nextElement();
                if ("host".equalsIgnoreCase(headername)) continue;
                if (headername.equalsIgnoreCase(HttpHeaders.CONTENT_LENGTH)) continue;

                /*if (headername.equalsIgnoreCase(org.apache.http.cookie.SM.COOKIE)) {
                	logger.debug("##cookie in get request header:" + headername);
                }*/
                httpGet.addHeader(headername, req.getHeader(headername));
                logger.debug("add get request header:" + headername + "=" + req.getHeader(headername));
            }
            Builder bd = null;
			if (this.connectTimeout > 0) {
				bd = bd == null ? RequestConfig.custom() :bd;
				bd.setConnectTimeout(connectTimeout);
			}
			if(this.socketTimeout > 0){
				(bd = bd == null ? RequestConfig.custom() :bd).setSocketTimeout(socketTimeout);
			}
			if(bd != null){
				httpGet.setConfig(bd.build());
			}
			chr = hc.execute(httpGet);
			HttpEntity he = chr.getEntity();
			if (he != null) {
				bodyContent = EntityUtils.toString(he,"utf-8");
			}
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		} finally {
			if (hc != null) {
				try {
					hc.close();
				} catch (IOException e) {
				}
			}
			if (chr != null) {
				try {
					chr.close();
				} catch (IOException e) {
				}
			}
		}
		logger.debug("bodyContent:",bodyContent);
		return bodyContent;
	}
	
	@Override
	public boolean shouldFilter() {
		return true;
	}

	@Override
	public int filterOrder() {
		return 0;
	}

	@Override
	public String filterType() {
		return "route";
	}

}
